# This code is so you can run the samples without installing the package
import sys
import os
import networkx
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))

# stdlib

#pyglet
from pyglet.gl import *

# cocos2d related
from cocos.scenes.transitions import *
from cocos.layer import Layer, ColorLayer
from cocos.scene import Scene
from cocos.director import director
from cocos.actions import *
from cocos.sprite import *
from cocos.menu import *
from cocos.text import *
from cocos.euclid import *
from monster import Character, Tatu, Monster
from random import randint
import HUD
import threading
from menu import MainMenu

__all__ = ['get_newgame']



class GameOver(Layer):
    is_event_handler = True
    def __init__(self):
        super( GameOver, self ).__init__()

        #background
        background = HUD.BackgroundLayer('TatuCarretaGameOverEscala.jpg')
        self.add(background, z=-1)

    def on_mouse_press (self, x, y, buttons, modifiers):
        """This function is called when any mouse button is pressed

        (x, y) are the physical coordinates of the mouse
        'buttons' is a bitwise or of pyglet.window.mouse constants LEFT, MIDDLE, RIGHT
        'modifiers' is a bitwise or of pyglet.window.key modifier constants
            (values like 'SHIFT', 'OPTION', 'ALT')
        """
        main_scene = Scene()
        over = MainMenu()
        main_scene.add(over, z=0)
        main_scene.add(HUD.BackgroundLayer('TatuCarretaRun.jpg'), z=0)
        director.push( FlipAngular3DTransition(
                        main_scene, 1.5 ) )


def end_game():
    main_scene = Scene()
    over = GameOver()
    main_scene.add(over, z=0)
    return main_scene

class GameWin(Layer):
    is_event_handler = True
    def __init__(self):
        super( GameWin, self ).__init__()

        #background
        background = HUD.BackgroundLayer('TatuCarretaWiinner.jpg')
        self.add(background, z=-1)

    def on_mouse_press (self, x, y, buttons, modifiers):
        """This function is called when any mouse button is pressed

        (x, y) are the physical coordinates of the mouse
        'buttons' is a bitwise or of pyglet.window.mouse constants LEFT, MIDDLE, RIGHT
        'modifiers' is a bitwise or of pyglet.window.key modifier constants
            (values like 'SHIFT', 'OPTION', 'ALT')
        """
        main_scene = Scene()
        over = MainMenu()
        main_scene.add(over, z=0)
        main_scene.add(HUD.BackgroundLayer('TatuCarretaRun.jpg'), z=0)
        director.push( FlipAngular3DTransition(
                        main_scene, 1.5 ) )


def win_game():
    main_scene = Scene()
    over = GameWin()
    main_scene.add(over, z=0)
    return main_scene

def get_newlevel(level):
    '''returns the game scene'''
    main_scene = Scene()
    level = Level(level)

    main_scene.add(level, z=0)
    return main_scene

class Level(Layer):
    is_event_handler = True
    def __init__(self, level_number):
        super( Level, self ).__init__()

        #background
        self.level = level_number
        bkg_image = "background%s.png"%level_number
        colition_image = "background%s-colition.png"%level_number
        background = HUD.BackgroundLayer(bkg_image, colition_image)
        self.add(background, z=-1)
        self.events = []
        self.garchers = []
        #bitmap mask and a* structure for pathfinding
        self.reptiles = []
        self.parents = []
        self.childrens = []
        self.create_graph(background.bitmap_mask)

        #level title
        label = Label('Level %s'%level_number, font_name='Edit Undo Line BRK',
                        font_size=30,
                        anchor_y='center',
                        anchor_x='center',
                        color=(204,164,164,255) )
        label.position = (400, 550)

        self.selected = None
        self.tatues = []

        self.character = Character('monster.png')
        self.character.setup_animation(1, 1)
        self.character.set_pos(200, 200)
        self.posx = 100
        self.posy = 240
        self.add(self.character.sprite)
        self.add(label)
        self.schedule(self.all_time)
        self.males = []
        self.females = []
        self.male_tatu = Tatu('Tatu_Male.png', gender = 'male')
        self.parents.append(self.male_tatu)
        self.males.append(self.male_tatu)
        self.male_tatu.setup_animation(12, 1)

        self.male_tatu.set_pos(*self.get_valid_coords([0, 400], [0, 600]))

        self.female_tatu = Tatu('Tatu_Female.png', gender = 'female')
        self.females.append(self.female_tatu)
        self.parents.append(self.female_tatu)
        self.female_tatu.setup_animation(12, 1)

        self.female_tatu.set_pos(*self.get_valid_coords([400, 800], [0, 600]))

        self.add(self.male_tatu.sprite)
        self.add(self.female_tatu.sprite)

        self.tatues.append(self.female_tatu)
        self.tatues.append(self.male_tatu)

        self.female_tatu.sprite.stop()
        self.male_tatu.sprite.stop()
        self.schedule_interval( self.monster_factory, 4 )

        self.number_of_monsters = 0

    def create_graph(self, bitmap_mask):
        self.G = networkx.Graph()

        print "Se empieza a cargar el mapa"
        for i in range(len(bitmap_mask) - 2):
            for j in range(len(bitmap_mask[i]) - 2):
                if bitmap_mask[i][j] == 0:
                    if bitmap_mask[i][j+1] == 0:
                        self.G.add_edge(i*len(bitmap_mask[i]) + j, i*len(bitmap_mask[i]) + j + 1)
                    if bitmap_mask[i+1][j] == 0:
                        self.G.add_edge( i * len(bitmap_mask[i]) + j, (i + 1) *len(bitmap_mask[i]) + j)
        i+=1
        for j in range(len(bitmap_mask[i]) - 2):
            if bitmap_mask[i+1][j] == 0:
                self.G.add_edge( i * len(bitmap_mask[i]) + j, (i + 1) *len(bitmap_mask[i]) + j)
        print "Se termino de cargar el mapa"


    def on_mouse_press (self, x, y, buttons, modifiers):
        """This function is called when any mouse button is pressed

        (x, y) are the physical coordinates of the mouse
        'buttons' is a bitwise or of pyglet.window.mouse constants LEFT, MIDDLE, RIGHT
        'modifiers' is a bitwise or of pyglet.window.key modifier constants
            (values like 'SHIFT', 'OPTION', 'ALT')
        """
        shall_move = True
        for tatu in self.tatues:
            if tatu.sprite.contains(x, y):
                self.selected = tatu
                shall_move = False
                break

        if self.selected and shall_move:

            current_x = int(round(self.selected.sprite.x))
            current_y = int(round(self.selected.sprite.y))

            self.selected.sprite.stop()
            self.selected.sprite.play()
            try:
                self.selected.sprite.do( MovePath(self.G,
                                              [current_x, current_y],
                                              [x, y],
                                              0.04))
            except:
                pass

        self.posx, self.posy = director.get_virtual_coordinates(x, y)
        self.character.set_pos(self.posx, self.posy)

        for garch in self.garchers:
            if garch.sprite.contains(x, y):
                garch.male.decopulate(garch.female,self)

    def monster_factory(self, dt):
        """Crea monstruos cuando es posible.

        Dependiendo de la configuracion que exista para el nivel, la cantidad
        de monstruos que se van a crear. La velocidad y aceleracion de cada uno
         de ellos es fija. Una vez creados los bichos no pueden ser eliminados.
        """

        if self.number_of_monsters <= self.level :

            try:
                self.trayectories = [self.get_valid_coords([0, 800], [0, 600]) for x in range(5)]
                self.reptile = Monster('reptile_ANIM.png')
                self.reptile.setup_animation(13, 1)
                seq = Repeat(MovePath(self.G, self.trayectories[0], self.trayectories[1],15) +
                            MovePath(self.G, self.trayectories[1], self.trayectories[2],15) +
                            MovePath(self.G, self.trayectories[2], self.trayectories[3],15) +
                            MovePath(self.G, self.trayectories[3], self.trayectories[4],15) +
                            MovePath(self.G, self.trayectories[4], self.trayectories[1],15))

                self.reptile.sprite.do(seq)
                self.reptiles.append(self.reptile)
                self.add(self.reptile.sprite)
                self.number_of_monsters = self.number_of_monsters + 1
            except:
                pass

    def all_time(self, dt):
        if len(self.childrens) == self.level:
            if self.level == 6:
                director.push( FlipAngular3DTransition(win_game(), 1.5 ) )
            else:
                director.push( FlipAngular3DTransition(get_newlevel(self.level +1), 1.5 ) )

        for male in self.males:
            for female in self.females:
                if female.sprite.get_rect().intersects(male.sprite.get_rect()):
                    if not male.copulating and not female.copulating:
                        print "copulate"
                        garch = male.copulate(female, self)
                        self.garchers.append(garch)
                else:
                    male.decopulate(female,self)
        for reptile in self.reptiles:
            for tatu in self.tatues:
                if reptile.sprite.get_rect().intersects(tatu.sprite.get_rect()):

                    if tatu.sprite in [child[1] for child in self.children]:
                        self.remove(tatu.sprite)
                        if tatu in self.parents:
                            director.push( FlipAngular3DTransition(end_game(), 1.5 ) )
                        if tatu in self.tatues:
                            del self.tatues[self.tatues.index(tatu)]
                        if tatu in self.females:
                            del self.females[self.females.index(tatu)]
                        if tatu in self.males:
                            del self.males[self.males.index(tatu)]
                        if tatu in self.childrens:
                            del self.childrens[self.childrens.index(tatu)]

    def get_valid_coords(self, x, y):
        coords = (randint(*x), randint(*y))
        while get_node_from_coords(*coords) not in self.G.nodes():
            coords = (randint(*x), randint(*y))
        return coords

class MovePath( IntervalAction ):
    def init(self, graph, coords_from=[], coords_to=[], duration=5):

        orig = get_node_from_coords(*coords_from)
        dest = get_node_from_coords(int(round(coords_to[0])), int(round(coords_to[1])))

        if orig in graph and dest in graph:
            self.path = networkx.algorithms.shortest_paths.astar.astar_path(
                graph,
                orig,
                dest
            )

            self.path = [get_coords_from_node(i) for i in self.path]
        else:
            self.path = []

        self.current_path_position = 0

        self.duration = duration

    def done(self):
        return self.current_path_position == len(self.path)

    def start(self):

        if self.path:
            self.start_position = self.target.position
            self.delta = Point2(*self.path[self.current_path_position]) - self.start_position

    def update(self, t):
        # Calculate rotation

        if self.path and self.current_path_position < len(self.path):
            where_to = self.path[self.current_path_position]

            if self.current_path_position == 0:
                prev_pos = Point2(*self.target.position)
            else:
                prev_pos = Point2(*self.path[self.current_path_position-1])

            next_pos = Point2(*where_to)

            dir_vector = next_pos - prev_pos

            direction = self.target.rotation
            if dir_vector.y > 0:
                #arriba
                direction = 0
            if dir_vector.y < 0:
                #abajo
                direction = 180
            if dir_vector.x > 0:
                #derecha
                direction = 90
            if dir_vector.x < 0:
                #izquierda
                direction = 270

            #import pdb;pdb.set_trace()
            new_direction = direction - self.target.rotation - 90
            if new_direction < -180:
                new_direction = 360 + new_direction
            elif new_direction > 180:
                new_direction = -(360 - new_direction)

            if new_direction != 0:
                self.target.rotation = self.target.rotation + new_direction * t

            # Move the object
            target_x = int(round(self.target.position[0]))
            target_y = int(round(self.target.position[1]))
            path_x = int(round(self.path[self.current_path_position][0]))
            path_y = int(round(self.path[self.current_path_position][1]))


            if target_x == path_x and target_y == path_y:
                self.current_path_position += 1
                self._elapsed = 0.0

                #if self.current_path_position < len(self.path):
                    ##self.start_position = self.target.position
                    #self.start_position = self.path[self.current_path_position-1]
                    #self.delta = Point2(*self.path[self.current_path_position]) - self.start_position

            else:
                self.delta = Point2(*self.path[self.current_path_position]) - self.target.position
                self.target.position = self.target.position + self.delta * t



    def get_coords_from_node(self, number):
        return int((number%40) * 20), int((29-(number/40)) * 20)

    def get_node_from_coords(self, x, y):
        return (x/20) + (40 * (29 - (y/20)))

#def vector2angle(v,w=None):
    #"""
       #Get the angle between the X axis and the direction defined by v.
       #The angle is in (-pi, pi] range
       #If w is given then it is taken as the reference
       #TODO: Check efficiency of this implementation
    #"""
    #if w != None:
        #ang2 = math.atan2(float(v[1]), float(v[0]))
        #ang1 = math.atan2(float(w[1]), float(w[0]))
        #dang = ang2 - ang1
        #while dang > math.pi :
            #dang = dang - 2.0*math.pi
        #while dang < -math.pi :
            #dang = dang + 2.0*math.pi

        #return dang
    #else:
        #return (atan2(float(v[1]), float(v[0])))

##def follow_path(path, callback):
    ###print "follow path"
    ##try:
        ##y, x = path.pop()
        ##y = 29 - y
    ##except:
        ##return CallFunc(callback)

    ##where_to = ((x*20), (y*20))
    ##return MoveTo( where_to, 0.2 ) + follow_path(path, callback)

##def move_object(_object, graph, orig, dest):
    ##print "Antes del path"
    ##path = networkx.algorithms.shortest_paths.astar.astar_path(graph, orig , dest)
    ##path.reverse()
    ##print "Despues del path"
    ##path = follow_path(path, _object.sprite.pause)

    ##if path:
        ##_object.sprite.do( path + follow_path(path, _object.sprite.pause) )

def get_coords_from_node(number):
    # Helper function to convert a node number to coordinates
    return int((number%40) * 20), int((29-(number/40)) * 20)

def get_node_from_coords(x, y):
    # Helper function to convert coordinates into a node number
    return (x/20) + (40 * (29 - (y/20)))

def get_newgame():
    '''returns the game scene'''
    main_scene = Scene()
    level = Level(1)

    main_scene.add(level, z=0)
    return main_scene



